Option Explicit

'################################################################
' Descriptor
' 真实表的表信息
'################################################################

Implements ISqlTranslateTableInfo

'################################################################
' Field
'################################################################
' 表名 token (这里只保存表名，表名前的 schema 忽略)
Private tableNameToken As SqlToken
' 别名token
Private aliasNameToken As SqlToken
' 原始表名名称
Private originTableName As String
' 转换后的表的目标类型名称
Private translatedTableName As String
' 非要求类型的表名称
Private reverseTypeTableName As String

' 保存名称映射:
'      转换类型名称 --> ISqlTranslateNameInfo
'      非要求类型名称 --> ISqlTranslateNameInfo
' Map<String, ISqlTranslateNameInfo>
Private selectColNameInfoMap As Object

' 有序的保存列名数据
' IObjArrayRef<IObjArrayRef<ISqlTranslateNameInfo>>
Private orderedColNameInfos As IObjArrayRef

'################################################################
' Init
'################################################################
' 初始化
Public Sub Init (ByRef tableNameTokenVal As SqlToken)
    ' 1. 检查参数
    If tableNameTokenVal Is Nothing Then
        Err.Raise 10001, Description:="parameter tableNameTokenVal must be not nothing"
        Exit Sub
    End If

    ' 保存数据
    Set tableNameToken = tableNameTokenVal

    ' 从 token 中获取并保存原始名称
    originTableName = tableNameToken.value

    ' 使用原始名称初始化 [转换后的名称]
    translatedTableName = originTableName

    reverseTypeTableName = ""
End Sub

'################################################################
' Implements ISqlTranslateTableInfo Function
'################################################################
' 设置别名 token
Public Sub ISQLTranslateTableInfo_setAliasNameToken(ByRef t As SqlToken)
    Set aliasNameToken = t
End Sub

' 获取别名 token
Public Function ISQLTranslateTableInfo_getAliasNameToken() As SqlToken
    Set ISQLTranslateTableInfo_getAliasNameToken = aliasNameToken
End Function

' 获取别名
Public Function ISQLTranslateTableInfo_getAliasName() As String
    If aliasNameToken Is Nothing Then
        ISQLTranslateTableInfo_getAliasName = ""
    Else
        ISQLTranslateTableInfo_getAliasName = aliasNameToken.value
    End If
End Function

' 读取原始的真实表名 （从 token 中读取）
Public Function ISQLTranslateTableInfo_getOriginTableName() As String
    ISQLTranslateTableInfo_getOriginTableName = originTableName
End Function

' 写入翻译后的真实表名
Public Sub ISQLTranslateTableInfo_setTranslatedTableName(ByRef name As String)
    ' 设置表的转换后名称
    translatedTableName = name
    ' 然后将转换后名称写入名称 token
    If Not tableNameToken Is Nothing Then
        tableNameToken.value = name
    End If
End Sub

' 读取翻译后的真实表名
Public Function ISQLTranslateTableInfo_getTranslatedTableName() As String
    ISQLTranslateTableInfo_getTranslatedTableName = translatedTableName
End Function

' 获取非要求类型的表名
Public Function ISQLTranslateTableInfo_getReverseTypeTableName() As String
    ISQLTranslateTableInfo_getReverseTypeTableName = reverseTypeTableName
End Function

' 返回当前表中的各类型列名称--列名称信息映射
' 映射的内容包括:
'    转换后名称 --> 列名称信息
'    非要求类型名称 --> 列名称信息
'    列别名(如果有的话) --> 列名称信息
' @param translateType 转换的类型(逻辑->物理=0, 物理->逻辑=1)
' @return Map<String, ISqlTranslateNameInfo>
Public Function ISQLTranslateTableInfo_getTableColNameInfoMap(ByRef em As ISqlEntityManager, ByVal translateType As Integer) As Object
        ' 1 获取表的转换后名称
        '     如果表的转换后名称是 "" , 则立刻终止
        If translatedTableName = "" Then
            'return null
            Exit Function
        End If

        If selectColNameInfoMap Is Nothing Then
            createColNameInfoCache em, translateType
        End If

        Set ISQLTranslateTableInfo_getTableColNameInfoMap = selectColNameInfoMap
End Function

' 在当前的表信息中搜索 colName 的转换后的名称
' 返回 null 表示搜索失败
' @param translateType 转换的类型(逻辑->物理=0, 物理->逻辑=1)
Public Function ISQLTranslateTableInfo_findColNameInfo(ByRef colName As String, ByRef em As ISqlEntityManager, ByVal translateType As Integer) As ISqlTranslateNameInfo
    ' 1. 获取表的转换后名称
    '     如果表的转换后名称是 "" , 则立刻终止
    ' 2. 如果缓存 selectColNameInfoMap 存在
    '     如果缓存中包含 colName，则返回缓存内的结果
    '     如果缓存中不包含 colName, 则返回 null
    ' 3. 持有 [表的变换后名称] + [列名] 到 em 中进行搜索
    '     1. 按照指定的变换方式进行检查
    '     2. 如果[colName] 已经是该表中的指定类型的列名
    '         1. 则保存到结果
    '         2. 然后获取 colName 的非要求类型名，并保存
    '     3. 否则尝试变换，然后返回结果
    '         1. 转换结果是 `""`，这表示转换失败，返回 null
    '         2. 转换结果不是 `""`，这表示转换成功
    '             1. 将转换结果保存为转换后名称
    '             2. 将当前 colName 保存为非要求类型名称

    ' 1 获取表的转换后名称
    '     如果表的转换后名称是 "" , 则立刻终止
    If translatedTableName = "" Then
        'return null
        Exit Function
    End If

    ' 2. 如果缓存 selectColNameInfoMap 存在，并包含 colName，则返回缓存内的结果
    If Not selectColNameInfoMap Is Nothing Then
        If selectColNameInfoMap.Exists(colName) Then
            ' return selectColNameInfoMap.Item(colName)
            Set ISQLTranslateTableInfo_findColNameInfo = selectColNameInfoMap.Item(colName)
        End If
        '} else {
        '    return null
        '}

        ' 在这里统一结束
        Exit Function
    End If

    ' 3.1 按照指定的变换方式进行检查
    Dim result As SqlTranslateNameSimpleInfo
    Dim translatedName As String
    If translateType = 0 Then
        ' 逻辑 -> 物理

        If em.isPhyItemNameByEntityPhyName(colName, translatedTableName) Then
            ' 3.2 如果[colName] 已经是该表中的指定类型的列名
            Set result = New SqlTranslateNameSimpleInfo

            ' 3.2.1 则保存到结果
            result.setTranslatedName colName
            ' 3.2.2 然后获取 colName 的非要求类型名，并保存
            result.setReverseTypeName em.getItemLogicNameByEntityPhyName(colName, translatedTableName)
        Else
            ' 3.3 否则尝试变换，然后返回结果
            translatedName = em.getItemPhyNameByEntityPhyName(colName, translatedTableName)

            ' 3.3.1 转换结果是 `""`，这表示转换失败，返回 null
            If translatedName = "" Then
                'return null
                Exit Function
            End If

            ' 3.3.2 转换结果不是 `""`，这表示转换成功
            Set result = New SqlTranslateNameSimpleInfo
            ' 3.3.1 将转换结果保存为转换后名称
            result.setTranslatedName translatedName
            ' 3.3.2 将当前 colName 保存为非要求类型名称
            result.setReverseTypeName colName

        End If

        Set ISQLTranslateTableInfo_findColNameInfo = result
        Exit Function
    Else
        ' 物理 -> 逻辑

        If em.isLogicItemNameByEntityLogicName(colName, translatedTableName) Then
            ' 3.2 如果[colName] 已经是该表中的指定类型的列名
            Set result = New SqlTranslateNameSimpleInfo

            ' 3.2.1 则保存到结果
            result.setTranslatedName colName
            ' 3.2.2 然后获取 colName 的非要求类型名，并保存
            result.setReverseTypeName em.getItemPhyNameByEntityLogicName(colName, translatedTableName)
        Else
            ' 3.3 否则尝试变换，然后返回结果
            translatedName = em.getItemLogicNameByEntityLogicName(colName, translatedTableName)

            ' 3.3.1 转换结果是 `""`，这表示转换失败，返回 null
            If translatedName = "" Then
                'return null
                Exit Function
            End If

            ' 3.3.2 转换结果不是 `""`，这表示转换成功
            Set result = New SqlTranslateNameSimpleInfo
            ' 3.3.1 将转换结果保存为转换后名称
            result.setTranslatedName translatedName
            ' 3.3.2 将当前 colName 保存为非要求类型名称
            result.setReverseTypeName colName
        End If

        Set ISQLTranslateTableInfo_findColNameInfo = result
        Exit Function
    End If
End Function

' 执行表信息的翻译
Public Sub ISQLTranslateTableInfo_translate(ByRef em As ISqlEntityManager, ByVal translateType As Integer)
    ' 1. translateType == 0, 逻辑->物理
    '     到 entityManger 中检查，[原始表名] 是不是逻辑名
    '     如果 [原始表名] 是逻辑名，则获取其对应的物理名: translatedTableName
    '          然后设置表名: setTranslatedTableName(translatedTableName)
    '     如果 [原始表名] 不是逻辑名，则直接设置
    '          setTranslatedTableName([原始表名])
    '          这里忽略 [原始表名] 的具体类型
    '          如果 [原始表名] 真的是物理名，则和变换方式相符
    '          如果 [原始表名] 只是一个 entityManager 中未记录的表名，则这里保持原样
    ' 2. translateType == 1, 物理->逻辑
    '     到 entityManger 中检查，realTableName 是不是物理名
    '     如果 [原始表名] 是物理名，则获取其对应的逻辑名: translatedTableName
    '          然后设置表名: setTranslatedTableName(translatedTableName)
    '     如果 [原始表名] 不是物理名，则直接设置
    '          setTranslatedTableName([原始表名])
    '          这里忽略 [原始表名] 的具体类型
    '          如果 [原始表名] 真的是逻辑名，则和变换方式相符
    '          如果 [原始表名] 只是一个 entityManager 中未记录的表名，则这里保持原样

    ' 对表名进行变换
    Dim tempName As String
    If translateType = 0 Then
        ' 逻辑 --> 物理
        ' 只在转换后名称存在时(转换结果不是空字符串)保存
        tempName = em.getEntityPhyName(originTableName)
        If tempName <> "" Then
            ISQLTranslateTableInfo_setTranslatedTableName tempName
        End If
    Else
        ' 物理 --> 逻辑
        tempName = em.getEntityLogicName(originTableName)
        If tempName <> "" Then
            ISQLTranslateTableInfo_setTranslatedTableName tempName
        End If
    End If

    ' 计算非要求类型名
    If translatedTableName = "" Then
        ' 如果两个名称相同，则需要搜索到非要求类型的名称，并保存

        translatedTableName = originTableName
        If translateType = 0 Then
            ' 逻辑->物理=0
            ' 在这里 translatedTableName 是物理名，需要将物理名转换成逻辑名
            ' 即: 物理 entity 名 ---> 逻辑 entity 名
            reverseTypeTableName = em.getEntityLogicName(translatedTableName)
        Else
            ' 物理->逻辑=1
            ' 在这里 translatedTableName 是逻辑名，需要将逻辑名转换成物理名
            ' 即: 逻辑 entity 名 ---> 物理 entity 名
            reverseTypeTableName = em.getEntityPhyName(translatedTableName)
        End If
    Else
        reverseTypeTableName = originTableName
    End If
End Sub

' 返回有序的列名信息
' 遇到全数据列需要主动展开
' IObjArrayRef<IObjArrayRef<ISqlTranslateNameInfo>> 额外的一层 IObjArrayRef 用来兼容
' 子查询表中的 union 中产生的多个子查询的列对象
' @param translateType 转换的类型(逻辑->物理=0, 物理->逻辑=1)
' @return IObjArrayRef<IObjArrayRef<ISqlTranslateNameInfo>>
Public Function ISQLTranslateTableInfo_getOrderedTableColNameInfos(ByRef em As ISqlEntityManager, ByVal translateType As Integer) As IObjArrayRef
    If orderedColNameInfos Is Nothing Then
        createColNameInfoCache em, translateType
    End If

    Set ISQLTranslateTableInfo_getOrderedTableColNameInfos = orderedColNameInfos
End Function
'################################################################
' Private Function
'################################################################
' 创建名称信息映射
Private Sub createColNameInfoCache(ByRef em As ISqlEntityManager, ByVal translateType As Integer)
    ' 1. 按照不同的转换类型获取【基础映射】:
    '    非要求类型名称 --> 转换后名称
    Dim translatedNameList As IPrimitiveArrayRef
    Dim reverseTypeNameList As IPrimitiveArrayRef
    If translateType = 0 Then
        ' 逻辑->物理=0
        Set translatedNameList = em.getItemPhyNames(translatedTableName)
        Set reverseTypeNameList = em.getItemLogicNames(translatedTableName)
    Else
        ' 物理->逻辑=1
        Set translatedNameList = em.getItemLogicNames(translatedTableName)
        Set reverseTypeNameList = em.getItemPhyNames(translatedTableName)
    End If

    ' 2. 合并各名称列表
    If Not translatedNameList Is Nothing And Not reverseTypeNameList Is Nothing Then
        ' 两种类型名称全有
        createColNameInfoCacheByNameList translatedNameList, reverseTypeNameList
    Else
        If Not translatedNameList Is Nothing Then
            createColNameInfoCacheByTranslatedName translatedNameList
        Else
            createColNameInfoCacheByReverseTypeName reverseTypeNameList
        End If
    End If

    ' 3. 如果已经被创建了，则再包装一层 ObjArrayRef，以表示一个表中的列名称信息
    If Not orderedColNameInfos Is Nothing Then
        Dim temp As IObjArrayRef
        set temp = UtilsArrayRef.createRealObjArrayRef(1)

        temp.Add orderedColNameInfos
        Set orderedColNameInfos = temp
    End If
End Sub

' 创建名称缓存
' 同时拥有两种类型的名称列表
' 这里需要 zip 两种名称
Private Sub createColNameInfoCacheByNameList(ByRef translatedNameList As IPrimitiveArrayRef, ByRef reverseTypeNameList As IPrimitiveArrayRef)
    ' 1. 计算两个列表中的最大数量 + 最小数量
    ' boolean isEqualCount = false
    ' boolean isTranslatedBig = false
    ' long maxCount = 0
    ' long minCount = 0
    Dim isEqualCount As Boolean
    Dim isTranslatedBig As Boolean
    Dim listMaxIndex As Long
    Dim listMinIndex As Long

    If translatedNameList.MaxIndex = reverseTypeNameList.MaxIndex Then
        isEqualCount = true

        listMaxIndex = translatedNameList.MaxIndex
        listMinIndex = translatedNameList.MaxIndex
    ElseIf translatedNameList.MaxIndex < reverseTypeNameList.MaxIndex Then
        listMaxIndex = reverseTypeNameList.MaxIndex
        listMinIndex = translatedNameList.MaxIndex

        isTranslatedBig = true
    Else
        listMaxIndex = translatedNameList.MaxIndex
        listMinIndex = reverseTypeNameList.MaxIndex
    End If

    ' 2. 初始化缓存
    Set selectColNameInfoMap = UtilsCreateSysObj.createMap
    Set orderedColNameInfos = UtilsArrayRef.createRealObjArrayRef(listMaxIndex)

    ' 3. 按照最小值获取遍历表
    Dim i As Long
    Dim translatedName As String
    Dim reverseTypeName As String
    Dim sni As SqlTranslateNameSimpleInfo
    For i = 0 To listMinIndex
        translatedName = translatedNameList.Item(i)
        reverseTypeName = reverseTypeNameList.Item(i)

        ' 3.1 创建一个名称信息
        Set sni = New SqlTranslateNameSimpleInfo
        sni.setTranslatedName translatedName
        sni.setReverseTypeName reverseTypeName

        ' 3.2 保存名称信息
        selectColNameInfoMap.Add translatedName, sni
        selectColNameInfoMap.Add reverseTypeName, sni

        orderedColNameInfos.Add sni
    Next

    ' 4. 如果是两个长度相等的表，则立刻结束
    If isEqualCount = True Then
        Exit Sub
    End If

    ' 5. 为长度更长的 list 中的剩余的数据创建名称信息
    If isTranslatedBig = True Then
        ' 处理剩余的转换后名称
        For i = (listMinIndex + 1) To listMaxIndex
            translatedName = translatedNameList.Item(i)

            ' 2.1 创建一个名称信息
            Set sni = New SqlTranslateNameSimpleInfo
            sni.setTranslatedName translatedName

            ' 2.2 保存名称信息
            selectColNameInfoMap.Add translatedName, sni
            orderedColNameInfos.Add sni
        Next
    Else
        ' 处理剩余的非要求类型名称
        For i = (listMinIndex + 1) To listMaxIndex
            reverseTypeName = reverseTypeNameList.Item(i)

            ' 2.1 创建一个名称信息
            Set sni = New SqlTranslateNameSimpleInfo
            sni.setReverseTypeName reverseTypeName

            ' 2.2 保存名称信息
            selectColNameInfoMap.Add reverseTypeName, sni
            orderedColNameInfos.Add sni
        Next
    End If
End Sub

' 创建名称缓存
' 只有转换后类型名称列表
Private Sub createColNameInfoCacheByTranslatedName(ByRef translatedNameList As IPrimitiveArrayRef)
    ' 1. 初始化缓存
    Set selectColNameInfoMap = UtilsCreateSysObj.createMap
    Set orderedColNameInfos = UtilsArrayRef.createRealObjArrayRef(translatedNameList.Count)

    ' 2. 填充缓存
    Dim i As Long
    Dim n As String
    Dim sni As SqlTranslateNameSimpleInfo
    With translatedNameList
        For i = 0 To .MaxIndex
            n = .Item(i)

            ' 2.1 创建一个名称信息
            Set sni = New SqlTranslateNameSimpleInfo
            sni.setTranslatedName n

            ' 2.2 保存名称信息
            selectColNameInfoMap.Add n, sni
            orderedColNameInfos.Add sni
        Next
    End With
End Sub

' 创建名称缓存
' 只有非要求类型名称列表
Private Sub createColNameInfoCacheByReverseTypeName(ByRef reverseTypeNameList As IPrimitiveArrayRef)
    ' 1. 初始化缓存
    Set selectColNameInfoMap = UtilsCreateSysObj.createMap
    Set orderedColNameInfos = UtilsArrayRef.createRealObjArrayRef(reverseTypeNameList.Count)

    ' 2. 填充缓存
    Dim i As Long
    Dim n As String
    Dim sni As SqlTranslateNameSimpleInfo
    With reverseTypeNameList
        For i = 0 To .MaxIndex
            n = .Item(i)

            ' 2.1 创建一个名称信息
            Set sni = New SqlTranslateNameSimpleInfo
            sni.setReverseTypeName n

            ' 2.2 保存名称信息
            selectColNameInfoMap.Add n, sni
            orderedColNameInfos.Add sni
        Next
    End With
End Sub